#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <arpa/inet.h>


#include "common.h"
#include "usb_device.h"

static int32 usb_check_bulk_io_ack(uint8 *buf, int32 len)
{
    if((NULL == buf) || (4 != len))
    {
        ERR("%s:%d:%s: the param is error!!!\r\n", __FILE__, __LINE__, __func__);
        return DRV_ERR;
    }

    if((0x55 != buf[0])&&(0x53 != buf[1])\
        &&(0x42 != buf[2])&&(0x53 != buf[3]))
    {
        return DRV_ERR;
    }

    return DRV_OK;
}

static int32 usb_read_bulk_io_ack(USB_DEV_STRUCT *this)
{
    uint8 buf[256] = {0x00};
    INTF_USB_CMD_STRUCT dev_cmd;
    //uint32 msg_len = 4;
    INTF_USB_HANDLE_STRUCT *usb_handle = NULL;

    if(NULL == this)
    {
        WD_ERR("the param is ERROR!\n");
        return DRV_ERR;
    }
    usb_handle = &(this->usb_handle);

    memset((uint8 *)&dev_cmd, 0x00, sizeof(INTF_USB_CMD_STRUCT));

    dev_cmd.buff = buf;
    dev_cmd.write_len = 0;
    dev_cmd.read_len = 4;
    dev_cmd.timeout = LIBUSB_BULK_TRANSFER_TIMEOUT;

    if(DRV_OK != intf_usb_bulk_transfer(usb_handle, &dev_cmd))
    {
        D_DBG("intf_usb_bulk_transfer return error!!!\n");
        return DRV_ERR;
    }

    if(0 >= dev_cmd.read_len)
    {
        D_DBG("not received data!!!\n");
        return DRV_ERR;
    }

    if(DRV_OK != usb_check_bulk_io_ack(dev_cmd.buff, 4))
    {
        D_DBG("not received BULK_IO_ACK!!!\n");
        return DRV_ERR;
    }

    this->bulk_io_ack_state = BULK_IO_ACK_1;

    return DRV_OK;
}


static int32 usb_prepare_send(USB_DEV_STRUCT *this, int32 msg_len)
{
    uint8 buf[256] = {0x55, 0x53, 0x42, 0x43, 0x00, 0x00, 0x00, 0x00};
    INTF_USB_CMD_STRUCT dev_cmd;
    INTF_USB_HANDLE_STRUCT *usb_handle = NULL;

    if(NULL == this)
    {
        WD_ERR("the param is ERROR!\n");
        return DRV_ERR;
    }
    usb_handle = &(this->usb_handle);

    if(0 >= msg_len)
    {
        WD_ERR("input param msg_len = %d, error!!!\n", msg_len);
        return DRV_ERR;
    }

    memset((uint8 *)&dev_cmd, 0x00, sizeof(INTF_USB_CMD_STRUCT));

    D_DBG("msg_len = %d\r\n",  msg_len);

    dev_cmd.buff = buf;
    buf[4] = BulkIoPhase_Out;
    buf[5] = msg_len & 0xff;
    buf[6] = (msg_len >> 8) & 0xff;
    buf[7] = (msg_len >> 16) & 0xff;


    dev_cmd.write_len = 8;
    dev_cmd.read_len = 0;
    dev_cmd.timeout = LIBUSB_BULK_TRANSFER_TIMEOUT;

    if(DRV_OK != intf_usb_bulk_transfer(usb_handle, &dev_cmd))
    {
        WD_ERR("send failed!!!\n");
        return DRV_ERR;
    }

    return DRV_OK;
}


static int32 usb_prepare_read(USB_DEV_STRUCT *this, int32 msg_len)
{
    uint8 buf[256] = {0x55, 0x53, 0x42, 0x43, 0x00, 0x00, 0x00, 0x00};
    INTF_USB_CMD_STRUCT dev_cmd;
    INTF_USB_HANDLE_STRUCT *usb_handle = NULL;

    if(NULL == this)
    {
        WD_ERR("the param is ERROR!\n");
        return DRV_ERR;
    }
    usb_handle = &(this->usb_handle);

    if(0 >= msg_len)
    {
        ERR("%s: input param msg_len = %d, error!!!\r\n", __func__, msg_len);
        return DRV_ERR;
    }

    memset((uint8 *)&dev_cmd, 0x00, sizeof(INTF_USB_CMD_STRUCT));

    //DBG("%s: msg_len = %d\r\n", __func__, msg_len);

    dev_cmd.buff = buf;
    buf[4] = BulkIoPhase_In;
    buf[5] = msg_len & 0xff;
    buf[6] = (msg_len >> 8) & 0xff;
    buf[7] = (msg_len >> 16) & 0xff;

    dev_cmd.write_len = 8;
    dev_cmd.read_len = 0;
    dev_cmd.timeout = LIBUSB_BULK_TRANSFER_TIMEOUT;

    if(DRV_OK != intf_usb_bulk_transfer(usb_handle, &dev_cmd))
    {
        WD_ERR("%s: send failed!!!\r\n", __func__);
        return DRV_ERR;
    }

    return DRV_OK;
}

int32 usb_send_data(USB_DEV_STRUCT *this, uint8 *buf, int32 len, int32 *write_len)
{
    INTF_USB_CMD_STRUCT dev_cmd;
    INTF_USB_HANDLE_STRUCT *usb_handle = NULL;

    if((NULL == this) || (NULL == buf) || (0 >= len))
    {
        ERR("%s:%d:%s: the param is error!!!\n", __FILE__, __LINE__, __func__);
        return DRV_ERR;
    }
    usb_handle = &(this->usb_handle);

    memset((uint8 *)&dev_cmd, 0x00, sizeof(INTF_USB_CMD_STRUCT));
    dev_cmd.buff = buf;
    dev_cmd.write_len = len;
    dev_cmd.read_len = 0;
    dev_cmd.timeout = LIBUSB_BULK_TRANSFER_TIMEOUT;

    if(DRV_OK != usb_prepare_send(this, len))
    {
        D_DBG("usb_prepare_send return error!!!\n");
        return DRV_ERR;
    }

    if(DRV_OK != intf_usb_bulk_transfer(usb_handle, &dev_cmd))
    {
        D_DBG("usb_dev_ioctl return error!!!\n");
        return DRV_ERR;
    }

    D_DBG("usb_read_bulk_io_ack.\n");
    if(DRV_OK != usb_read_bulk_io_ack(this))
    {
        D_DBG("not received BULK_IO_ACK!!!\n");
        return DRV_ERR;
    }

    *write_len = dev_cmd.write_len;

    return DRV_OK;
}


int32 usb_read_data(USB_DEV_STRUCT *this, uint8 *buf, int32 len, int32 *read_len)
{
    INTF_USB_CMD_STRUCT dev_cmd;
    //uint16 i = 0;
    //uint16 cycle_count = len/64 + 1;
    //int32 read_byte_len = 0;
    INTF_USB_HANDLE_STRUCT *usb_handle = NULL;

    if((NULL == this) || (NULL == buf) || (0 >= len))
    {
        WD_ERR("the param is ERROR!\n");
        return DRV_ERR;
    }
    usb_handle = &(this->usb_handle);

    memset((uint8 *)&dev_cmd, 0x00, sizeof(INTF_USB_CMD_STRUCT));

    if(DRV_OK != usb_prepare_read(this, len))
    {
        WD_ERR("%s:%d:%s: usb_prepare_read return error!!!\r\n", __FILE__, __LINE__, __func__);
        return DRV_ERR;
    }

    dev_cmd.buff = buf;
    dev_cmd.write_len = 0;
    dev_cmd.read_len = len;
    dev_cmd.timeout = LIBUSB_BULK_TRANSFER_TIMEOUT;

    if(DRV_OK != intf_usb_bulk_transfer(usb_handle, &dev_cmd))
    {
        WD_ERR("%s:%d:%s: usb_dev_ioctl return error!!!\r\n", __FILE__, __LINE__, __func__);
        return DRV_ERR;
    }

    if(DRV_OK != usb_read_bulk_io_ack(this))
    {
        WD_ERR("%s:%d:%s: not received BULK_IO_ACK!!!\r\n", __FILE__, __LINE__, __func__);
        return DRV_ERR;
    }

    *read_len = dev_cmd.read_len;

    return DRV_OK;
}


static int32 usb_dev_param_init(USB_DEV_STRUCT *this)
{
    memset((uint8 *)this, 0x00, sizeof(USB_DEV_STRUCT));
    this->usb_handle.handle = NULL;

    return DRV_OK;
}


int32 usb_dev_open(USB_DEV_STRUCT *this, INTF_USB_ID_STRUCT *id_table)
{
    int i;
    int ret = -1;
    //libusb_device_handle *handle = NULL;

    for (i = 0; id_table[i].idVendor != 0; i++)
    {
        if(0 == intf_usb_open(&id_table[i], &(this->usb_handle)))
        {
            ret = 0;
            this->usb_id = id_table[i];
            break;
        }
    }

    if(0 != ret)
    {
        this->usb_handle.handle = NULL;
    }

    return ret;
}

int32 *usb_get_dev_handle(USB_DEV_STRUCT *this)
{
    return (int32 *)(this->usb_handle.handle);
}


int32 usb_dev_close(USB_DEV_STRUCT *this)
{

    if(NULL == this->usb_handle.handle)
    {
        //D_DBG("%s: %s not open!!!\r\n", __func__, DEV_NAME);
        return DRV_ERR;
    }

    intf_usb_close(&(this->usb_handle));

    return DRV_OK;
}

int32 usb_dev_set_mcu_type(USB_DEV_STRUCT *this, uint8 type)
{
    this->mcu_type = type;

    return 0;
}


uint8 usb_dev_get_mcu_type(USB_DEV_STRUCT *this)
{
    return this->mcu_type;
}

int usb_dev_disable_autosuspend(USB_DEV_STRUCT *this)
{
    char set_control_cmd[500] = {0};

    if(0 == strlen(this->dev_port_path))
    {
        D_DBG("not get dev_port_path!\r\n");
        return -1;
    }

    snprintf(set_control_cmd, 500, "echo \"on\" > %s%s", this->dev_port_path, "/power/control");
    WD_DBG("%s\n", set_control_cmd);
    if(0 > system(set_control_cmd))
        WD_ERR("exec failed!\n");

    return 0;
}

int usb_dev_enable_autosuspend(USB_DEV_STRUCT *this)
{
    char set_control_cmd[500] = {0};

    if(0 == strlen(this->dev_port_path))
    {
        D_DBG("not get dev_port_path!\r\n");
        return -1;
    }

    if(1 == this->autosuspend_flag)
    {
        snprintf(set_control_cmd, 500, "echo \"auto\" > %s%s", this->dev_port_path, "/power/control");
        WD_LOG("%s\n", set_control_cmd);
        if(0 > system(set_control_cmd))
            WD_ERR("exec failed!\n");

        memset(set_control_cmd, 0x00, 500);
        snprintf(set_control_cmd, 500, "echo 2 > %s%s", this->dev_port_path, "/power/autosuspend");
        WD_LOG("%s\n", set_control_cmd);
        if(0 > system(set_control_cmd))
            WD_ERR("exec failed!\n");

        memset(set_control_cmd, 0x00, 500);
        snprintf(set_control_cmd, 500, "echo 2000 > %s%s", this->dev_port_path, "/power/autosuspend_delay_ms");
        WD_LOG("%s\n", set_control_cmd);
        if(0 > system(set_control_cmd))
            WD_ERR("exec failed!\n");
    }
    else
    {
        usb_dev_disable_autosuspend(this);
    }

    return 0;
}



int32 usb_set_autosuspend_flag(USB_DEV_STRUCT *this, uint8 flag)
{
    this->autosuspend_flag = flag;
    return 0;
}


int32 usb_dev_module_init(USB_DEV_STRUCT *this)
{
    if(DRV_OK != usb_dev_param_init(this))
    {
        WD_ERR("usb_dev_param_init failed!!!\n");
        return DRV_ERR;
    }

    return DRV_OK;
}


